package io.github.novaframe.lock;

import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

/**
 * @author Melvin
 * @since 2018-01-07 1:26
 */
public class SecKillImpl implements SeckillInterface {
    static Map<Long, Long> inventory;

    static {
        inventory = new HashMap<>();
        inventory.put(10000001L, 10000l); //
        inventory.put(10000002L, 10000l);
    }

    @Override
    public void secKill(String arg1, Long arg2) {
        //最简单的秒杀，这里仅作为demo示例
        reduceInventory(arg2);
    }

    /**
     * 模拟秒杀操作，姑且认为一个秒杀就是将库存减一，实际情景要复杂的多
     * @param commodityId
     * @return
     */
    public Long reduceInventory(Long commodityId) {
        inventory.put(commodityId, inventory.get(commodityId) - 1);
        return inventory.get(commodityId);
    }

    public static void main(String[] args) {
        SecKillImpl secKill = new SecKillImpl();
        secKill.test();
    }

    public void test() {
        long commidityId1 = 10000001;
        long commidityId2 = 10000002;

        int threadCount = 10000;
        int splitPoint = 5000;
        CountDownLatch endCount = new CountDownLatch(threadCount);
        // 一个同步工具类，它允许一个或多个线程一直等待，直到其他线程的操作执行完后再执行。
        // CountDownLatch通过计数器来实现，计数器为0时结束等待
        CountDownLatch beginCount = new CountDownLatch(1);
        SecKillImpl testClass = new SecKillImpl();

        Thread[] threads = new Thread[threadCount];
        //起500个线程，秒杀第一个商品

        // init lock
        MapSimpleLock.initLock(commidityId1 + "");
        MapSimpleLock.initLock(commidityId2 + "");

        for (int i = 0; i < splitPoint; i++) {
            threads[i] = new Thread(() -> {
                try {
                    //等待在一个信号量上，挂起
                    beginCount.await();

                    //用动态代理的方式调用secKill方法
                    SeckillInterface proxy = (SeckillInterface) Proxy.newProxyInstance(SeckillInterface.class.getClassLoader(),
                            new Class[]{SeckillInterface.class}, new CacheLockInterceptorTest(testClass, null));
                    proxy.secKill("test", commidityId1);
                    endCount.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            threads[i].start();

        }
        //再起500个线程，秒杀第二件商品
        for (int i = splitPoint; i < threadCount; i++) {
            threads[i] = new Thread(new Runnable() {
                public void run() {
                    try {
                        //等待在一个信号量上，挂起
                        beginCount.await();
                        //用动态代理的方式调用secKill方法
                        SeckillInterface proxy = (SeckillInterface) Proxy.newProxyInstance(SeckillInterface.class.getClassLoader(),
                                new Class[]{SeckillInterface.class}, new CacheLockInterceptorTest(testClass, null));
                        proxy.secKill("test", commidityId2);
                        //testClass.testFunc("test", 10000001L);
                        endCount.countDown();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
            threads[i].start();
        }

        long startTime = System.currentTimeMillis();
        //主线程释放开始信号量，并等待结束信号量，这样做保证1000个线程做到完全同时执行，保证测试的正确性
        beginCount.countDown();

        try {
            //主线程等待结束信号量
            endCount.await();
            //观察秒杀结果是否正确
            System.out.println(SecKillImpl.inventory.get(commidityId1));
            System.out.println(SecKillImpl.inventory.get(commidityId2));
            System.out.println("error count" + CacheLockInterceptor.ERROR_COUNT);
            System.out.println("total cost " + (System.currentTimeMillis() - startTime));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
